HTTPS 的实现过程和原理
HTTPS 概述
HTTPS(HyperText Transfer Protocol Secure)是 HTTP 的安全版本,它在 HTTP 和 TCP 之间增加了一个 TLS/SSL 加密层,为网络通信提供了三个核心安全保障:
- 机密性(Confidentiality):数据传输过程中被加密,防止窃听
- 完整性(Integrity):确保数据在传输过程中未被篡改
- 身份认证(Authentication):验证服务器身份,防止中间人攻击
TLS 握手过程详解
HTTPS 的核心是 TLS 协议,TLS 握手是建立安全连接的关键步骤。让我们详细分析这个过程:
TLS 握手核心步骤解析
第一阶段:协商加密参数
// TLS Client Hello 包含的信息
type ClientHello struct {
Version uint16 // TLS版本 (如 TLS 1.3)
Random [32]byte // 客户端随机数
CipherSuites []uint16 // 支持的密码套件列表
CompressionMethods []byte // 支持的压缩方法
Extensions []Extension // 扩展信息(SNI等)
}
客户端会列出自己支持的所有加密算法,包括:
- 密钥交换算法:RSA、ECDH、DHE 等
- 身份验证算法:RSA、ECDSA、DSA 等
- 对称加密算法:AES-128、AES-256、ChaCha20 等
- 消息认证码:SHA-256、SHA-384 等
第二阶段:服务器响应和证书验证
服务器从客户端提供的列表中选择一个密码套件,并发送自己的数字证书。证书包含:
// 数字证书的核心信息
type Certificate struct {
Version int
SerialNumber *big.Int
Subject pkix.Name // 证书持有者信息
Issuer pkix.Name // 证书颁发者信息
NotBefore time.Time // 有效期开始时间
NotAfter time.Time // 有效期结束时间
PublicKey interface{} // 服务器公钥
Signature []byte // CA的数字签名
}
第三阶段:密钥生成和验证
这是整个握手过程中最关键的步骤。客户端和服务器需要生成相同的会话密钥:
// 预主密钥生成(RSA密钥交换为例)
func generatePreMasterSecret() []byte {
// 客户端生成48字节的随机预主密钥
preMasterSecret := make([]byte, 48)
preMasterSecret[0] = 0x03 // TLS版本号高位
preMasterSecret[1] = 0x04 // TLS版本号低位
rand.Read(preMasterSecret[2:]) // 46字节随机数
return preMasterSecret
}
// 主密钥推导
func deriveKeys(preMasterSecret, clientRandom, serverRandom []byte) SessionKeys {
// 使用PRF(伪随机函数)推导主密钥
masterSecret := prf(preMasterSecret, "master secret",
append(clientRandom, serverRandom...))
// 从主密钥推导会话密钥
keyBlock := prf(masterSecret, "key expansion",
append(serverRandom, clientRandom...))
return SessionKeys{
ClientWriteKey: keyBlock[0:16], // 客户端加密密钥
ServerWriteKey: keyBlock[16:32], // 服务器加密密钥
ClientWriteIV: keyBlock[32:48], // 客户端初始化向量
ServerWriteIV: keyBlock[48:64], // 服务器初始化向量
}
}
数字证书验证机制
数字证书是 HTTPS 安全性的基石,它解决了"如何确认服务器身份"的问题。
证书链验证过程
// 证书链验证伪代码
func verifyCertificateChain(serverCert *Certificate, certChain []*Certificate) error {
// 1. 检查证书有效期
now := time.Now()
if now.Before(serverCert.NotBefore) || now.After(serverCert.NotAfter) {
return errors.New("证书已过期或尚未生效")
}
// 2. 验证域名匹配
if !verifyHostname(serverCert, requestedHostname) {
return errors.New("证书域名不匹配")
}
// 3. 验证证书链
currentCert := serverCert
for _, parentCert := range certChain {
// 使用父证书的公钥验证当前证书的签名
if !verifySignature(currentCert, parentCert.PublicKey) {
return errors.New("证书签名验证失败")
}
currentCert = parentCert
}
// 4. 验证根证书是否受信任
if !isRootCATrusted(currentCert) {
return errors.New("根证书不受信任")
}
return nil
}
为什么需要证书链?
直接从根 CA 为每个网站签发证书在实际中不可行,因为:
- 安全性考虑:根 CA 私钥极其重要,需要离线保存
- 可扩展性:全球有数百万个网站需要证书
- 管理便利性:中间 CA 可以专门负责特定类型的证书